# -*- coding: utf-8 -*-

"""
Module implementing MainWindow.
"""

from PyQt4 import QtCore
from PyQt4.QtGui import QMainWindow, QListWidgetItem, QPixmap, QIcon, QTableWidgetItem
from PyQt4.QtCore import pyqtSignature, SIGNAL, QUrl, QObject, QString
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt4.QtXml import QXmlSimpleReader, QXmlInputSource, QXmlDefaultHandler

from Ui_mainwindow import Ui_MainWindow

class MainWindow(QMainWindow, Ui_MainWindow):
    """
    Class documentation goes here.
    """
    def __init__(self, parent = None):
        """
        Constructor
        """
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        
        self.subsonic = Subsonic()
        self.connect(self.subsonic, SIGNAL('parsedArtist(PyQt_PyObject)'), self.receiveArtist)
        self.connect(self.subsonic, SIGNAL('parsedAlbum(PyQt_PyObject)'), self.receiveAlbum)
        self.connect(self.subsonic, SIGNAL('parsedTrack(PyQt_PyObject)'), self.receiveTrack)
        self.connect(self.subsonic, SIGNAL('receivedCoverArt(PyQt_PyObject)'), self.receiveCoverArt)
        
        #self.subsonic.ping()
        self.subsonic.getIndexes()
        
    def receiveArtist(self, record):
        '''
        Receive an artist and add it to the list.
        '''
        print 'Record: %s' % record
        newItem = QListWidgetItem()
        newItem.setText(record['artist'])
        newItem.id = record['id']
        self.lwArtists.addItem(newItem)
        return
        
    def receiveAlbum(self, record):
        '''
        Receive an album and add it to the list.
        '''
        print 'Record: %s' % record
        self.subsonic.coverArt = QPixmap()
        self.subsonic.getCoverArt(record['coverArt'])
        while self.subsonic.coverArt.isNull():
            QtCore.QCoreApplication.processEvents()
        newItem = QListWidgetItem()
        newItem.setText(record['title'])
        newItem.setIcon(QIcon(self.subsonic.coverArt))
        newItem.id = record['id']
        newItem.coverArt = record['coverArt']
        self.lwAlbums.addItem(newItem)
        
#        self.subsonic.getCoverArt(record['coverArt'])
        return
        
    def receiveTrack(self, record):
        '''
        Receive a track and add it to the list.
        '''
        print 'Record: %s' % record
        rowcount = self.lwTracks.rowCount()
        self.lwTracks.insertRow(rowcount)
        self.lwTracks.setItem(rowcount, 0, QTableWidgetItem(record['track']))
        self.lwTracks.setItem(rowcount, 1, QTableWidgetItem(record['title']))
        self.lwTracks.setItem(rowcount, 2, QTableWidgetItem(record['duration']))
        self.lwTracks.setItem(rowcount, 3, QTableWidgetItem(record['artist']))
        self.lwTracks.setItem(rowcount, 4, QTableWidgetItem(record['album']))
        self.lwTracks.setItem(rowcount, 5, QTableWidgetItem(record['size']))
        self.lwTracks.setItem(rowcount, 6, QTableWidgetItem(record['bitrate']))
        self.lwTracks.setItem(rowcount, 7, QTableWidgetItem(record['contentType']))
        self.lwTracks.setItem(rowcount, 8, QTableWidgetItem(record['path']))
#        newItem = QListWidgetItem()
#        newItem.setText(record['title'])
#        newItem.id = record['id']
#        self.lwTracks.addItem(newItem)
        return
        
    def receiveCoverArt(self, image):
        '''
        Receive cover art and attempt to add it to the view.
        '''
        pass
        
    def updateArtists(self):
        self.lwArtists.clear()
        print self.subsonic.artists
        print 'Total artists: %s' % len(self.subsonic.artists)
        artists = self.subsonic.artists
        for id in artists.keys():
            newitem = QListWidgetItem()
            newitem.setText(artists[id]['artist'])
            newitem.id = id
            self.lwArtists.addItem(newitem)
        self.lwArtists.sortItems()
    
    @pyqtSignature("")
    def on_pushButton_released(self):
        """
        Slot documentation goes here.
        """
        self.updateArtists()
    
    @pyqtSignature("QListWidgetItem*")
    def on_lwArtists_itemClicked(self, item):
        """
        Slot documentation goes here.
        """
        print 'clicked %s with id %s' % (str(item.text()), item.id)
        self.lwAlbums.clear()
        self.lwTracks.clearContents()
        for row in xrange(0, self.lwTracks.rowCount() - 1):
            self.lwTracks.removeRow(row)
        self.subsonic.getMusicDirectory(str(item.id))
    
    @pyqtSignature("QListWidgetItem*")
    def on_lwAlbums_itemClicked(self, item):
        """
        Slot documentation goes here.
        """
        self.lwTracks.clearContents()
        for row in xrange(0, self.lwTracks.rowCount() - 1):
            self.lwTracks.removeRow(row)
        self.subsonic.getMusicDirectory(str(item.id))

class Subsonic(QObject):
    '''
    An object to communicate with the Subsonic server.
    '''
    USERNAME = 'testuser'
    PASSWORD = 'testpass'
    PROTOCOL = '1.4.0'
    CLIENT = 'PySubsonic'
    FORMAT = 'xml'
    DEFPARMS = 'u=%s&p=%s&v=%s&c=%s' % (USERNAME, PASSWORD, PROTOCOL, CLIENT)
    SERVER = 'http://192.168.1.64:4040/rest/'
    
    def __init__(self, parent = None):
        '''
        Descriptions goes here.
        '''
        print 'Subsonic object created...'
        self.manager = QNetworkAccessManager()
        self.manager.finished.connect(self.readResponse)
        self.manager.authenticationRequired.connect(self.errors)
        self.manager.sslErrors.connect(self.errors)
        self.manager.proxyAuthenticationRequired.connect(self.errors)
        
        self.artists = {}
        QObject.__init__(self, parent)
        
        
    def errors(self):
        print 'Something went wrong...'
 
    def showProgress(self, rec, tot):
        pass
#        print 'Update: %s of %s' % (str(rec), str(tot))

    def readResponse(self, reply):
        '''
        Connected to self.http's finished() signal.'
        '''
        print 'I has a reply!'
        type = str(reply.header(QNetworkRequest.ContentTypeHeader).toString())
        print 'Type: %s' % type
        if type.split(';')[0] == 'text/xml':
            # XML data returned.
            print 'XML response...'

            input = QXmlInputSource()
            replytext = reply.readAll()
            print 'REPLY:\n%s' % replytext
            input.setData(replytext)
            
            error = None
            try:
                handler = XmlHandler(self)
                parser = QXmlSimpleReader()
                parser.setContentHandler(handler)
                parser.setErrorHandler(handler)
                if not parser.parse(input):
                    raise ValueError, handler.error
            except (IOError, OSError, ValueError), e:
                error = 'Failed to import: %s' % e
            finally:
                if error is not None:
                    return False, error
        else:
            # Not XML. Must be binary data...
            print 'Binary data?'
            data = reply.readAll()
            self.coverArt.loadFromData(data)
#            
#            pixmap = QPixmap()
#            if pixmap.loadFromData(data):
#                icon = QIcon(pixmap)
#                self.emit(SIGNAL('receivedCoverArt(PyQt_PyObject)'), icon)
                
    def ping(self):
        '''
        Used to test connectivity with the server. Takes no extra parameters.
        Returns an empty <subsonic-response> element on success.
        '''
        url = '%s%s%s' % (self.SERVER, 'ping.view?', self.DEFPARMS)
        self.response = self.manager.get(QNetworkRequest(QUrl(url)))
        self.response.downloadProgress.connect(self.showProgress)
        print 'Done'
        
    def getIndexes(self, musicFolderId=None, ifModifiedSince=None):
        '''
        Returns an indexed structure of all artists.
        
        Parameters:
        musicFolderId - If specified, only return artists in the music folder with the given ID. See getMusicFolders.
        ifModifiedSince - If specified, only return a result if the artist collection has changed since the given time.
        '''
        url = '%s%s%s' % (self.SERVER, 'getIndexes.view?', self.DEFPARMS)
        self.response = self.manager.get(QNetworkRequest(QUrl(url)))
        self.response.downloadProgress.connect(self.showProgress)
        print 'Done'
        
    def getMusicDirectory(self, id):
        '''
        Returns a listing of all files in a music directory. Typically used to get list of albums for an artist, or list of
        songs for an album.
        
        Parameters:
        id - A string which uniquely identifies the music folder. Obtained by calls to getIndexes or getMusicDirectory.
        '''
        url = '%s%s%s%s%s' % (self.SERVER, 'getMusicDirectory.view?', self.DEFPARMS, '&id=', id)
        self.response = self.manager.get(QNetworkRequest(QUrl(url)))
        self.response.downloadProgress.connect(self.showProgress)
        print 'Done'
   
    def getCoverArt(self, id, size=None):
        '''
        Returns a cover art image (binary on success, XML on error).MIMEImage
        
        Parameters:
        id - A string which uniquely identifies the cover art file to download. Obtained by calls to getMusicDirectory.
        size - If specified, scale image to this size.
        '''
        if size:
            url = '%s%s%s%s%s%s%s' % (self.SERVER, 'getCoverArt.view?', self.DEFPARMS, '&id=', id, '&size=', size)
        else:
            url = '%s%s%s%s%s' % (self.SERVER, 'getCoverArt.view?', self.DEFPARMS, '&id=', id)
        self.response = self.manager.get(QNetworkRequest(QUrl(url)))
        self.response.downloadProgress.connect(self.showProgress)        
        
class XmlHandler(QXmlDefaultHandler):
    '''
    Parse XML tags in Subsonic server replies.
    '''
    def __init__(self, parent):
        super(XmlHandler, self).__init__()
        self.parent = parent
        self.text = QString()
        self.error = None
        
    def clear(self):
        self.name = None
        self.id = None
        
    def startElement(self, namespaceURI, localname, qName, attributes):
        if qName == 'artist':
            self.clear()
            self.name = attributes.value('name')
            self.id = str(attributes.value('id'))
        elif qName == 'child':
            self.clear()
            self.id = str(attributes.value('id'))
            self.parentId = str(attributes.value('parent'))
            self.title = str(attributes.value('title'))
            self.isDir = str(attributes.value('isDir'))
            self.coverArt = str(attributes.value('coverArt'))
            self.artist = str(attributes.value('artist'))
            self.duration = str(attributes.value('duration'))
            self.album = str(attributes.value('album'))
            self.bitrate = str(attributes.value('bitrate'))
            self.track = str(attributes.value('track'))
            self.size = str(attributes.value('size'))
            self.contentType = str(attributes.value('contentType'))
            self.suffix = str(attributes.value('suffix'))
            self.path = str(attributes.value('path'))
        return True
            
    def characters(self, text):
        self.text += text
        return True
        
    def endElement(self, namespaceURI, localname, qName):
        if qName == 'artist':
            if self.name is None or self.id is None:
                raise ValueError, 'incomplete record'
#            self.parent.artists[self.id] = {'artist': self.name, 'albums': {}}
            record = {'artist': self.name, 'id': self.id}
            self.parent.emit(SIGNAL('parsedArtist(PyQt_PyObject)'), record)
            self.clear()
        elif qName == 'child':
            if self.id is None or self.title is None or self.isDir is None or \
                            self.coverArt is None or self.artist is None:
                raise ValueError, 'incomplete record'
            if self.isDir == 'true':
                record = {
                        'id': self.id, 
                        'title': self.title, 
                        'coverArt': self.coverArt, 
                        'artist': self.artist, 
                        'isDir': self.isDir, 
                        'parent': self.parentId}
                self.parent.emit(SIGNAL('parsedAlbum(PyQt_PyObject)'), record)
            else:
                record = {
                        'id': self.id, 
                        'title': self.title, 
                        'coverArt': self.coverArt, 
                        'artist': self.artist, 
                        'isDir': self.isDir, 
                        'parent': self.parentId, 
                        'duration': self.duration, 
                        'bitrate': self.bitrate, 
                        'size': self.size, 
                        'suffix': self.suffix, 
                        'path': self.path, 
                        'album': self.album, 
                        'track': self.track, 
                        'contentType': self.contentType}
                self.parent.emit(SIGNAL('parsedTrack(PyQt_PyObject)'), record)
        return True
            
        
    def fatalError(self, exception):
        self.error = 'parse error at line %d column %d: %s' % (
                                        exception.lineNumber(), exception.columnNumber(), exception.message())
        return False
    
        
        
